home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- Linux loader
-
- Author: Markus Gutschke (gutschk@math.uni-muenster.de)
- Date: Sep/95
-
- **************************************************************************/
-
- #include "netboot.h"
-
- #define LINUX_IMAGE_ADDR ((char *)0x10000L)
- #define XTRACMDS ((char *)0x98000L)
- #define LINUX_BOOTSECTOR ((char *)0x90000L)
- #define CMDLINEMAGIC 0xA33F
- #define CMDLINE ((unsigned short *)(LINUX_BOOTSECTOR+0x20))
- #define BOOTHEADER (((boot_header_t *)0x90200L)[-1])
- #define SECTOR_SIZE 512
- #define MAGIC 0xAA55
- #define SETUP_MAGIC 0x5A5AAA55
-
- typedef struct {
- unsigned char filler;
- unsigned char setup_sects;
- unsigned short int root_flags;
- unsigned short int syssize;
- unsigned short int swap_dev;
- unsigned short int ram_size;
- unsigned short int vid_mode;
- unsigned short int root_dev;
- unsigned short int boot_flag;
- } boot_header_t;
-
- struct bootp_t bootp_reply;
-
- char *linux_add_cmdline(char *s)
- {
- #ifdef NETBOOT32
- if (!s) *XTRACMDS = '\000';
- else if (*s) {
- char *ptr = XTRACMDS;
- while (*ptr) ptr++;
- *ptr++ = ' ';
- sprintf(ptr,"%s",s); }
- return(XTRACMDS);
- #endif
- }
-
- #ifdef NETBOOT32
- static int strncmp(const unsigned char *s1,const unsigned char *s2,int n)
- {
- while (n--) {
- if (*s1 != *s2) return(*s1 - *s2);
- else if (!*s1) return(0);
- else {s1++; s2++;}}
- return(0);
- }
-
- static void linux_cmdline(char *cmdline)
- {
- char *s,*d,initargs = 0;
-
- for (s = XTRACMDS; ;) { /* check for parameters to init */
- if ((!*s || *s == ' ') && (initargs&1)) initargs = 2;
- if (!*s) break;
- if (*s == '=') {initargs &= ~1; while (*s && *s != ' ') s++;}
- else if (*s++ != ' ') {
- if (!(initargs&1) && !strncmp(s-1,"vga=",4)) {
- int vga = 0;
- d = s + 3;
- if (!strncmp(d,"ASK",3)) vga = -3;
- else if (!strncmp(d,"EXTENDED",8)) vga = -2;
- else if (!strncmp(d,"NORMAL",6)) vga = -1;
- else {
- if (*d == '-') d++;
- while (*d >= '0' && *d <= '9') { vga = 10*vga+*d-'0'; d++; }
- if (s[3] == '-') vga = -vga; }
- *((unsigned short *)(LINUX_BOOTSECTOR+506)) = vga; }
- initargs |= 1; } }
- for (s = nfsdiskless.root_hostnam;*s&&s[1];s++);
- sprintf(cmdline,
- "%sBOOT_IMAGE=%s%s%s ramdisk=0 nfsroot=%s,rsize=%d,wsize=%d,%s,%s nfsaddrs=%I:%I:%I:%I:%s %s",
- initargs&2 ? "" : "auto ",
- /* htonl(nfsdiskless.root_saddr.sin_addr.s_addr), */
- nfsdiskless.root_hostnam,
- *s == '/' ? "" : "/",
- *kernel == '/' ? kernel + 1 : kernel,
- nfsdiskless.root_hostnam,
- nfsdiskless.root_args.rsize,
- nfsdiskless.root_args.wsize,
- nfsdiskless.root_args.flags & NFSMNT_SOFT ? "soft" : "hard",
- nfsdiskless.root_args.flags & NFSMNT_INT ? "intr" : "nointr",
- arptable[ARP_CLIENT].ipaddr,
- htonl(nfsdiskless.root_saddr.sin_addr.s_addr),
- arptable[ARP_GATEWAY].ipaddr,
- htonl(netmask),
- nfsdiskless.my_hostnam,
- XTRACMDS);
- for (s = d = cmdline; ;) { /* remove multiple space characters */
- if (*s == ' ') {while (s[1] == ' ') s++; if (!s[1]) s++;}
- if (!(*d++ = *s++)) break; }
- *((unsigned short *)(LINUX_BOOTSECTOR+504)) = 0; /* no ramdisk */
- *((unsigned short *)(LINUX_BOOTSECTOR+508)) = 0x00FF; /* mount root on nfs */
- return;
- }
- #endif
-
- int load_linux(int root_mount_port,int swap_mount_port,
- int root_nfs_port,char *kernel_handle)
- {
- #ifdef NETBOOT32
- int err, offset, read_size, count;
- char *addr,*cmdline;
-
- /* Linux boot sector and setup code has to be loaded to address 0x90000 */
- if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, kernel_handle, 0,
- SECTOR_SIZE,LINUX_BOOTSECTOR)) != SECTOR_SIZE) {
- readerr:
- printf("Unable to read %s: ",kernel);
- nfs_err(err);
- bootmenu:
- longjmp(jmp_bootmenu,1);
- }
- if (BOOTHEADER.boot_flag != MAGIC)
- return(0);
- /* Boot sector contains size information for setup code */
- offset = SECTOR_SIZE;
- read_size = SECTOR_SIZE;
- count = BOOTHEADER.setup_sects;
- while (count--) {
- if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, kernel_handle,
- offset, read_size, LINUX_BOOTSECTOR + offset)) !=
- read_size) {
- if (err < 0) {
- goto readerr; }
- goto bootmenu; }
- offset += err; }
- for (count = SECTOR_SIZE-4; *(unsigned int *)(LINUX_BOOTSECTOR-
- SECTOR_SIZE+offset+count) !=
- SETUP_MAGIC;)
- if (count-- < 0)
- goto bootmenu;
- /* Construct Linux's command line */
- cmdline = LINUX_BOOTSECTOR + offset;
- CMDLINE[0] = CMDLINEMAGIC;
- CMDLINE[1] = cmdline - LINUX_BOOTSECTOR;
- linux_cmdline(cmdline);
- while (*cmdline) putchar(*cmdline++);
- printf("\r\n");
- /* Kernel image will be loaded to address 0x10000; it will automatically
- be relocated to 0x100000 by the setup code */
- read_size = NFS_READ_SIZE;
- count = 16*BOOTHEADER.syssize;
- addr = LINUX_IMAGE_ADDR;
- printf("Loading compressed kernel image");
- while (count >= 16) {
- if (read_size > count) read_size = count;
- if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, kernel_handle,
- offset, read_size, addr)) != read_size) {
- if (err < 0) goto readerr;
- if (read_size-err >= 16)
- goto bootmenu; }
- if (!(offset & 0x3C00)) putchar('.');
- offset += err;
- count -= err;
- addr += err; }
- /* Linux wants to mount swapspace and rootfilesystem by itself; so unmount */
- /* all mounted NFS filesystems */
- nfs_umountall(ARP_ROOTSERVER, root_mount_port);
- if (arptable[ARP_SWAPSERVER].ipaddr &&
- arptable[ARP_SWAPSERVER].ipaddr != arptable[ARP_ROOTSERVER].ipaddr)
- nfs_umountall(ARP_SWAPSERVER, swap_mount_port);
- /* Linux kernel has to be started in real-mode */
- printf(" \r\nStarting...\r\n");
- start_linux();
- /* printf("*** %s execute failure ***\n",kernel); */
- goto bootmenu;
- #endif
- }
-
- /* Define some structures used by tftp loader */
-
- union infoblock
- {
- unsigned short s[256];
- unsigned long l[128];
- struct imgheader
- {
- unsigned long magic;
- unsigned char length;
- unsigned char r[3];
- struct { unsigned short bx, ds; } location;
- struct { unsigned short ip, cs; } execaddr;
- } i;
- };
-
- struct segheader
- {
- unsigned char length;
- unsigned char vendortag;
- unsigned char reserved;
- unsigned char flags;
- unsigned long loadaddr;
- unsigned long imglength;
- unsigned long memlength;
- };
-
- /* The following are static because linux_tftp is called for each block
- and we need to retain info across calls */
-
- static enum loadmode { Munknown, Mlinear, Mtagged } mode = Munknown;
- static unsigned char segflags;
- static unsigned long seglen;
- static Address curaddr, last0, last1, execaddr, hdraddr, segaddr;
-
- int linux_tftp(unsigned int block, unsigned char *data, int len)
- {
- int i;
-
- if (block == 1)
- {
- union infoblock *ibp = (union infoblock *)data;
- if (ibp->l[0] == 0x1B031336L)
- {
- /* the apparently unnecessary cast is to avoid a
- bcc bug where the short not promoted as it should be */
- segaddr = (((Address)ibp->i.location.ds) << 4) + ibp->i.location.bx;
- #ifdef NETBOOT32
- bcopy(data, (void *)segaddr, len);
- #endif
- #ifdef NETBOOT16
- bcopyf(data, segaddr, len);
- #endif
- execaddr = ibp->l[3];
- hdraddr = ibp->l[2];
- last1 = last0 = segaddr + 512;
- segaddr += ((ibp->i.length & 0x0F) << 2)
- + ((ibp->i.length & 0xF0) >> 2);
- segflags = 0;
- seglen = 0;
- mode = Mtagged;
- return (1);
- }
- else if (ibp->s[255] == 0xAA55)
- {
- #ifdef NETBOOT32
- bcopy(data, (void *)0x7C00, len);
- #endif
- #ifdef NETBOOT16
- bcopyf(data, 0x7C00L, len);
- #endif
- execaddr = hdraddr = 0x7C00000L;
- curaddr = 0x10000L;
- mode = Mlinear;
- return (1);
- }
- return (0);
- }
- else switch (mode)
- {
- case Mlinear:
- if (len <= 0)
- {
- #ifdef NETBOOT32
- xstart(execaddr, hdraddr, (Address)&bootp_reply);
- #endif
- #ifdef NETBOOT16
- xstart(execaddr, hdraddr, &bootp_reply, RELOC>>4);
- #endif
- longjmp(jmp_bootmenu, 1);
- }
- #ifdef NETBOOT32
- bcopy(data, (void *)curaddr, len);
- #endif
- #ifdef NETBOOT16
- bcopyf(data, curaddr, len);
- #endif
- if (curaddr += 512 >= 0x98000L)
- curaddr = 0x100000L;
- break;
- case Mtagged:
- while (seglen == 0)
- {
- struct segheader sh;
- if (segflags & 0x04)
- {
- #ifdef NETBOOT32
- xstart(execaddr, hdraddr, (Address)&bootp_reply);
- #endif
- #ifdef NETBOOT16
- xstart(execaddr, hdraddr, &bootp_reply, RELOC>>4);
- #endif
- longjmp(jmp_bootmenu, 1);
- }
- #ifdef NETBOOT32
- sh = *((struct segheader *)segaddr);
- #endif
- #ifdef NETBOOT16
- fbcopy(segaddr, &sh, sizeof(struct segheader));
- #endif
- seglen = sh.imglength;
- if ((segflags = sh.flags & 0x03) == 0)
- curaddr = sh.loadaddr;
- else if (segflags == 0x01)
- curaddr = last1 + sh.loadaddr;
- else if (segflags == 0x02)
- curaddr = (Address)(memsize() * 1024L + 0x100000L)
- - sh.loadaddr;
- else
- curaddr = last0 - sh.loadaddr;
- last1 = (last0 = curaddr) + sh.memlength;
- segflags = sh.flags;
- segaddr += ((sh.length & 0x0F) << 2)
- + ((sh.length & 0xF0) >> 2);
- }
- i = (seglen > len) ? len : seglen;
- #ifdef NETBOOT32
- bcopy(data, (void *)curaddr, i);
- #endif
- #ifdef NETBOOT16
- bcopyf(data, curaddr, i);
- #endif
- seglen -= i;
- curaddr += i;
- break;
- case Munknown:
- return (0);
- }
- return (1);
- }
-